package com.project.authentication.controller;


import com.project.authentication.dto.ReqDto;
import com.project.authentication.dto.ReqParam;
import com.project.authentication.dto.TokenParam;
import com.project.authentication.dto.UserDto;
import com.project.authentication.entity.UserLog;
import com.project.authentication.service.UserLogService;
import com.project.authentication.util.NetworkUtil;
import com.project.authentication.util.PostRestResult;
import com.project.authentication.util.RestResult;
import com.project.authentication.util.Sm2Util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.*;

@Slf4j
@RestController
public class AuthController {

    protected HttpServletRequest request;

    protected HttpServletResponse response;

    @Resource
    UserLogService userLogService;

    @ModelAttribute
    public void setReqAndResp(HttpServletRequest request, HttpServletResponse response) {
        this.request = request;
        this.response = response;
    }

    /**
     * 获取token
     */
    @PostMapping("/auth/token/get")
    public PostRestResult getToken(@RequestBody ReqParam param) {
        if (StringUtils.isEmpty(param.getUserId())) {
            return PostRestResult.error("userId为空");
        }
        if (param.getTimestamp() == null) {
            return PostRestResult.error("timestamp为空");
        }
        if (StringUtils.isEmpty(param.getPrivateKey())) {
            return PostRestResult.error("privateKey为空");
        }
        if (StringUtils.isEmpty(param.getSign())) {
            return PostRestResult.error("sign为空");
        }
        try {
            log.info("签名解密开始");
            //将十六进制私钥串转换为 BCECPrivateKey 私钥对象
            BCECPrivateKey bcecPrivateKey = Sm2Util.getBCECPrivateKeyByPrivateKeyHex(param.getPrivateKey());
            String data = Sm2Util.decrypt(bcecPrivateKey, param.getSign(), 2);
            log.info("签名解密结果:" + data);
            if (!StringUtils.isEmpty(data) && data.contains("&") && data.contains("=")) {
                String[] params = data.split("&");
                if (params.length < 2) {
                    return PostRestResult.error("sign格式不正确");
                }
                String[] values1 = params[0].split("=");
                if (values1.length < 2) {
                    return PostRestResult.error("sign格式不正确");
                }
                log.info("userId:" + values1[1]);
                if (!param.getUserId().equals(values1[1])) {
                    return PostRestResult.error("sign格式不正确");
                }
                String[] values2 = params[1].split("=");
                if (values2.length < 2) {
                    return PostRestResult.error("sign格式不正确");
                }
                log.info("timestamp:" + values2[1]);
                if (param.getTimestamp().compareTo(Long.parseLong(values2[1])) != 0) {
                    return PostRestResult.error("sign格式不正确");
                }
                UserLog userLog = new UserLog();
                Date loginTime = new Date(Long.parseLong(values2[1]));
                userLog.setLogindt(loginTime);
                Calendar calendar = Calendar.getInstance();
                calendar.setTimeInMillis(Long.parseLong(values2[1]));
                calendar.add(Calendar.HOUR_OF_DAY, 12);
                ReqDto dto = new ReqDto();
                dto.setExpireTime(calendar.getTime());
                userLog.setLogoutdt(calendar.getTime());

                // 生成随机SM2公钥私钥
                KeyPair keyPair = Sm2Util.createECKeyPair();
                PublicKey publicKey = keyPair.getPublic();
                //获取65字节非压缩缩的十六进制公钥串(0x04)
                String publicKeyHex = Hex.toHexString(((BCECPublicKey) publicKey).getQ().getEncoded(false));
                log.info("随机SM2公钥：" + publicKeyHex);
                PrivateKey privateKey = keyPair.getPrivate();
                //获取32字节十六进制私钥串
                String privateKeyHex = ((BCECPrivateKey) privateKey).getD().toString(16);
                log.info("随机SM2私钥：" + privateKeyHex);
                userLog.setUid(privateKeyHex);
                //将十六进制公钥串转换为 BCECPublicKey 公钥对象
                String token = "userId=" + param.getUserId() + "&" + "expireTime=" + String.valueOf(dto.getExpireTime().getTime());
                BCECPublicKey bcecPublicKey = Sm2Util.getECPublicKeyByPublicKeyHex(publicKeyHex);
                String encryptToken = Sm2Util.encrypt(bcecPublicKey, token, 2);
                log.info("token：" + encryptToken);
                dto.setToken(encryptToken);
                userLog.setSessionid(encryptToken);
                userLog.setHostaddress(NetworkUtil.getIpAddress(request));
                userLogService.insert(userLog);
                return PostRestResult.ok(dto);
            } else {
                return PostRestResult.error("sign格式不正确");
            }
        } catch (Exception e) {
            return PostRestResult.error(e.getMessage());
        }
    }

    /**
     * 验证token
     */
    @PostMapping("/auth/token/check")
    public RestResult checkToken(@RequestBody TokenParam param) {
        if (StringUtils.isEmpty(param.getToken())) {
            return RestResult.error("token为空");
        }
        List<UserLog> userLogs = userLogService.getByToken(param.getToken());
        if (userLogs.size() > 0) {
            Calendar calendar = Calendar.getInstance();
            long now = calendar.getTime().getTime();
            if (now > userLogs.get(0).getLogoutdt().getTime()) {
                return RestResult.error("token已过期,请重新获取");
            }
            String privateKey = userLogs.get(0).getUid();
            //将十六进制私钥串转换为 BCECPrivateKey 私钥对象
            BCECPrivateKey bcecPrivateKey = Sm2Util.getBCECPrivateKeyByPrivateKeyHex(privateKey);
            String data = Sm2Util.decrypt(bcecPrivateKey, param.getToken(), 2);
            log.info("token解密结果:" + data);
            if (!StringUtils.isEmpty(data) && data.contains("&") && data.contains("=")) {
                String[] params = data.split("&");
                if (params.length < 2) {
                    return RestResult.error("token不正确");
                }
                String[] values1 = params[0].split("=");
                if (values1.length < 2) {
                    return RestResult.error("token不正确");
                }
                log.info("userId:" + values1[1]);
                String[] values2 = params[1].split("=");
                if (values2.length < 2) {
                    return RestResult.error("token不正确");
                }
                log.info("timestamp:" + values2[1]);
                UserDto userDto = new UserDto();
                userDto.setUid(values1[1]);
                userDto.setPage(param.getPage());
                userDto.setExpireTime(userLogs.get(0).getLogoutdt());
                return RestResult.ok(userDto);
            } else {
                return RestResult.error("token不正确");
            }
        } else {
            return RestResult.error("token不存在");
        }
    }

    /**
     * 生成随机公钥密钥及密文
     */
    @PostMapping("/auth/sign/create")
    public RestResult keyCreate() {
        // 生成随机SM2公钥私钥
        KeyPair keyPair = Sm2Util.createECKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        //获取65字节非压缩缩的十六进制公钥串(0x04)
        String publicKeyHex = Hex.toHexString(((BCECPublicKey) publicKey).getQ().getEncoded(false));
        PrivateKey privateKey = keyPair.getPrivate();
        //获取32字节十六进制私钥串
        String privateKeyHex = ((BCECPrivateKey) privateKey).getD().toString(16);
        long timestamp = System.currentTimeMillis();
        String token = "userId=admin&timestamp=" + timestamp;
        BCECPublicKey bcecPublicKey = Sm2Util.getECPublicKeyByPublicKeyHex(publicKeyHex);
        String encryptToken = Sm2Util.encrypt(bcecPublicKey, token, 2);
        Map<String, String> map = new HashMap<>();
        map.put("publicKey", publicKeyHex);
        map.put("privateKey", privateKeyHex);
        map.put("sign", encryptToken);
        map.put("userId", "admin");
        map.put("timestamp", String.valueOf(timestamp));
        return RestResult.ok(map);
    }
}
